#ifndef __ARG_H__
  #define __ARG_H__

#include "exceptions.h"
#include "macros.h"
#include "global_variables.h"

using namespace std;

//! Any number of parameters up to 7, made easy!

/*!
The idea is to use this class instead of C style variable arguments (va_start, va_arg, ...).
This makes sure that no crashes occur in case of type mismatch.
In case of datatype mismatches you will get compile errors instead of runtime crashes.
This class should only be used with basic data types like: int, short, long, double, string, ...

Implementation date: 2009

Usage:

\code
void method(const arg<int, double> &a) { ... }

method(arg<int, double>(17, 3.1415, 2.718));
\endcode
*/

template <class T, class U = T, class V = U, class W = V, class X = W, class Y = X, class Z = Y>
class arg {
private:
  const T *p1;
  const U *p2;
  const V *p3;
  const W *p4;
  const X *p5;
  const Y *p6;
  const Z *p7;

  //! assignment operator: not allowed
  arg &operator=(const arg &a);

public:
  //! returns the number of arguments
  unsigned short size() const {
    if (p1 == 0)
      return 0;
    else if (p2 == 0)
      return 1;
    else if (p3 == 0)
      return 2;
    else if (p4 == 0)
      return 3;
    else if (p5 == 0)
      return 4;
    else if (p6 == 0)
      return 5;
    else if (p7 == 0)
      return 6;
    else
      return 7;
  } // size

  const T &get_value1() const {
    INDEX_CHECK(0, size());
    return *p1;
  }

  const U &get_value2() const {
    INDEX_CHECK(1, size());
    return *p2;
  }

  const V &get_value3() const {
    INDEX_CHECK(2, size());
    return *p3;
  }

  const W &get_value4() const {
    INDEX_CHECK(3, size());
    return *p4;
  }

  const X &get_value5() const {
    INDEX_CHECK(4, size());
    return *p5;
  }

  const Y &get_value6() const {
    INDEX_CHECK(5, size());
    return *p6;
  }

    const Z &get_value7() const {
    INDEX_CHECK(6, size());
    return *p7;
  }

  //! constructor with one argument
  explicit arg(const T &p1) : p1(&p1), p2(0), p3(0), p4(0), p5(0), p6(0), p7(0) {
    // nothing to do
  } // arg

  //! constructor with two arguments
  explicit arg(const T &p1, const U &p2) : p1(&p1), p2(&p2), p3(0), p4(0), p5(0), p6(0), p7(0) {
    // nothing to do
  } // arg

  //! constructor with three arguments
  explicit arg(const T &p1, const U &p2, const V &p3) : p1(&p1), p2(&p2), p3(&p3), p4(0), p5(0), p6(0), p7(0) {
    // nothing to do
  } // arg

  //! constructor with four arguments
  explicit arg(const T &p1, const U &p2, const V &p3, const W &p4) : p1(&p1), p2(&p2), p3(&p3), p4(&p4), p5(0), p6(0), p7(0) {
    // nothing to do
  } // arg

  //! constructor with five arguments
  explicit arg(const T &p1, const U &p2, const V &p3, const W &p4, const X &p5) : p1(&p1), p2(&p2), p3(&p3), p4(&p4), p5(&p5), p6(0), p7(0) {
    // nothing to do
  } // arg

  //! constructor with six arguments
  explicit arg(const T &p1, const U &p2, const V &p3, const W &p4, const X &p5, const Y &p6) : p1(&p1), p2(&p2), p3(&p3), p4(&p4), p5(&p5), p6(&p6), p7(0) {
    // nothing to do
  } // arg

  //! constructor with seven arguments
  explicit arg(const T &p1, const U &p2, const V &p3, const W &p4, const X &p5, const Y &p6, const Z &p7) : p1(&p1), p2(&p2), p3(&p3), p4(&p4), p5(&p5), p6(&p6), p7(&p7) {
    // nothing to do
  } // arg

  //! destructor (non-virtual: please do not inherit from this class)
  ~arg() {
    // nothing to do
  } // ~arg
}; // class arg

#endif
